import axios from 'axios'
import { clone, mergeAll, omit, is } from 'ramda'
import { AxiosCanceler } from './axiosCancel'
import { genBaseConfig } from './encryptionTool'

import type { AxiosInstance, AxiosResponse, AxiosError, AxiosInterceptorOptions } from 'axios'
import type {
	RequestConfig,
	RequestHooksConfig,
	CustomAxiosRequestConfig,
	CustomInterceptors,
	CustomInterceptorManager,
	InterceptorsManagerMap,
	IResult,
} from './types'

export function axiosErrorIs(error: any): error is AxiosError<IResult<any>, any> {
	return error.isAxiosError
}

const axiosCanceler = new AxiosCanceler()

// 默认拦截器
const defaultInterceptors: CustomInterceptors = {
	requestInterceptors: (config) => {
		config.cancel && axiosCanceler.addPending(config)
		return config
	},
	requestInterceptorsCatch: (error) => Promise.reject(error),
	responseInterceptors: (response) => {
		axiosCanceler.removePending(response.config)
		return Promise.resolve(response)
	},
	responseInterceptorsCatch: (error) => {
		if (error && error.response) {
			const status = error.response.status
			const errorMessageMap = {
				400: '请求错误',
				401: '未授权，请重新登录',
				403: '拒绝访问',
				404: '请求不存在',
				405: '请求未允许',
				408: '请求超时',
				500: '服务器错误',
				501: '服务未实现',
				502: '网络错误',
				503: '服务不可用',
				504: '网络超时',
				505: 'HTTP版本不受支持',
			}
			error.message = `${errorMessageMap[status] || '请求出错'}!(${status})`
		}
		return Promise.reject(error)
	},
}

class AxiosRequest {
	private instance: AxiosInstance
	private readonly config: RequestConfig
	private readonly interceptorsManagerMap: InterceptorsManagerMap
	constructor(config: RequestConfig) {
		this.interceptorsManagerMap = new Map()
		this.config = config
		this.instance = axios.create(config)
		this.setupInterceptors('$_defaultInterceptors', defaultInterceptors)
		this.setupInterceptors('$_axiosRequestInterceptors', this.config.interceptors)
	}
	/**
	 * 获取Axios实例
	 *
	 * @returns Axios实例
	 */
	getInstance(): AxiosInstance {
		return this.instance
	}
	/**
	 * 设置基础URL
	 *
	 * @param url 基础URL
	 */
	setBaseURL(url) {
		this.instance.defaults.baseURL = url
	}
	/**
	 * 设置请求头
	 *
	 * @param headers 请求头对象
	 * @returns 无返回值
	 */
	setHeader(headers: any): void {
		if (!this.instance) return
		console.log('setHeader---1', this.instance.defaults.headers)
		Object.assign(this.instance.defaults.headers, headers)
		console.log('setHeader---2', this.instance.defaults.headers, headers)
	}
	/**
	 * 设置拦截器
	 *
	 * @param key 拦截器唯一标识
	 * @param customInterceptors 自定义拦截器
	 * @param interceptorRequestOptions 请求拦截器选项
	 * @param interceptorResponseOptions 响应拦截器选项
	 * @returns 无返回值
	 */
	setupInterceptors(
		key: string,
		customInterceptors?: CustomInterceptors,
		interceptorRequestOptions?: AxiosInterceptorOptions,
		interceptorResponseOptions?: AxiosInterceptorOptions
	) {
		if (!is(Object, customInterceptors)) return

		interceptorRequestOptions = {
			runWhen: (config) => {
				return true
			},
			synchronous: false, // 拦截器是否异步执行
		}
		const customInterceptorsManager = this.useCustomInterceptorManager(
			customInterceptors,
			interceptorRequestOptions,
			interceptorResponseOptions
		)

		const interceptorsMapItem = {
			requestInterceptorId: this.interceptorsManagerMap.size,
			responseInterceptorId: this.interceptorsManagerMap.size,
			customInterceptorsManager,
		}
		const has = this.interceptorsManagerMap.has(key)
		!has && this.interceptorsManagerMap.set(key, interceptorsMapItem)
	}
	/**
	 * 销毁指定 key 的拦截器
	 *
	 * @param key 拦截器 key
	 */
	teardownInterceptors(key: string) {
		const interceptorsMapItem = this.interceptorsManagerMap.get(key)
		if (interceptorsMapItem) {
			const { requestInterceptorId, responseInterceptorId, customInterceptorsManager } = interceptorsMapItem
			customInterceptorsManager.eject(requestInterceptorId, responseInterceptorId)
			this.interceptorsManagerMap.delete(key)
		}
	}
	/**
	 * 自定义拦截器管理器
	 *
	 * @param customInterceptors 自定义拦截器
	 * @param interceptorRequestOptions 请求拦截器选项
	 * @param interceptorResponseOptions 响应拦截器选项
	 * @returns 自定义拦截器管理器
	 */
	useCustomInterceptorManager<T extends AxiosInterceptorOptions = AxiosInterceptorOptions>(
		customInterceptors?: CustomInterceptors,
		interceptorRequestOptions?: T,
		interceptorResponseOptions?: T
	): CustomInterceptorManager {
		const use = (requestOptions?: T, responseOptions?: T) => {
			const requestInterceptorId = this.instance.interceptors.request.use(
				(config) => customInterceptors?.requestInterceptors?.(config as CustomAxiosRequestConfig) || config,
				(error) => customInterceptors?.requestInterceptorsCatch?.(error) || error,
				requestOptions || interceptorRequestOptions
			)

			const responseInterceptorId = this.instance.interceptors.response.use(
				(response) => customInterceptors?.responseInterceptors?.(response) || response,
				(error) => customInterceptors?.responseInterceptorsCatch?.(error) || error,
				responseOptions || interceptorResponseOptions
			)

			return { requestInterceptorId, responseInterceptorId }
		}

		const eject = (requestInterceptorId, responseInterceptorId) => {
			this.instance.interceptors.request.eject(requestInterceptorId)
			this.instance.interceptors.response.eject(responseInterceptorId)
		}

		return { use, eject }
	}
	/**
	 * @description: 函数的描述
	 * @interface T 请求参数
	 * @interface D 响应数据
	 * @param { RequestConfig<D> } 请求配置
	 * @returns {Promise<T>}
	 */
	request<T = any, D = any>(config: RequestConfig<T, D>): Promise<IResult<D> | AxiosResponse<D, T>> {
		const chain = [config.beforeRequest, config.beforeRequestCatch, this.config.beforeRequest, this.config.beforeRequestCatch]
		let newConfig = mergeAll([this.config, config])
		let baseConfig = clone(genBaseConfig(newConfig))
		const { url, afterResponseThen, afterResponseCatch, afterResponseFinally } = newConfig

		let len = chain.length
		let i = 0
		while (i < len) {
			const onFulfilled = chain[i++] as RequestHooksConfig['beforeRequest']
			const onRejected = chain[i++] as RequestHooksConfig['beforeRequestCatch']
			try {
				if (is(Function, onFulfilled)) {
					baseConfig = onFulfilled(baseConfig)
				}
			} catch (error) {
				is(Function, onRejected) && onRejected(error)
			}
		}

		newConfig = mergeAll([newConfig, baseConfig])

		this.setupInterceptors('$_customRequestInterceptors', config.interceptors)

		if (this.interceptorsManagerMap.size > 0) {
			const keys = newConfig.sortable ? [...this.interceptorsManagerMap.keys()].reverse() : [...this.interceptorsManagerMap.keys()]
			keys.forEach((key, i) => {
				const interceptorsMapItem = this.interceptorsManagerMap.get(key)
				if (interceptorsMapItem) {
					const { requestInterceptorId, responseInterceptorId } = interceptorsMapItem.customInterceptorsManager.use()
					interceptorsMapItem.requestInterceptorId = requestInterceptorId
					interceptorsMapItem.responseInterceptorId = responseInterceptorId
					this.interceptorsManagerMap.set(key, null)
				}
			})
		}
		return new Promise((resolve, reject) => {
			this.instance
				.request<T, AxiosResponse<D, T>, D>(newConfig)
				.then(async (res) => {
					if (is(Function, afterResponseThen)) {
						try {
							const ret = afterResponseThen(res, baseConfig)
							resolve(ret instanceof Promise ? await ret : ret)
						} catch (err) {
							reject(err || new Error('request error!'))
						}
						// reject(new Error('request error!'))
					} else {
						resolve(res)
					}
				})
				.catch((e) => {
					const error = is(Function, afterResponseCatch) ? afterResponseCatch(e) : e
					reject(error)
				})
				.finally(() => {
					is(Function, afterResponseFinally) && afterResponseFinally()
				})
		})
	}
}

export default AxiosRequest
